package com.mylogistics.common.filter;


import com.auth0.jwt.exceptions.TokenExpiredException;
import com.mylogistics.common.Utils.JWTUtil;
import com.mylogistics.common.Utils.ResponseUtil;
import com.mylogistics.common.vo.ResultData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * JWT过滤器
 */
@Component
public class MySecurityJWTFilter extends OncePerRequestFilter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        //1，从请求的头获取token
        String token = httpServletRequest.getHeader("Authorization");
        //2，通过token验证用户是否登录成功

        if (token != null) {
            //验证token的有效性
            String username = JWTUtil.getAccount(token);
            //3，验证成功之后，根据用户的账号查询用户的信息，
            // 存放到security对应的格式中，即可验证登录成功
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                //应该从数据库中查询
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                try {
                    //验证token是否有效
                    if (JWTUtil.verify(token)) {
                        // 将用户信息存入 authentication，方便后续校验
                        // 创建带权限的token直接会定义成登陆成功
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                        // 将 authentication 存入 ThreadLocal，方便后续获取用户信息
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                } catch (TokenExpiredException e) {
                    //验证是否需要创建新token（token是否有效）
                    if (JWTUtil.getCreateTime(token)) {
                        //创建新token
                        String newToken = JWTUtil.create(username, token);
                        //将新token放入Authorization的请求头中
                        httpServletResponse.setHeader("Authorization", newToken);
                        // 将用户信息存入 authentication，方便后续校验
                        // 创建带权限的token直接会定义成登陆成功
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                        // 将 authentication 存入 ThreadLocal，方便后续获取用户信息
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    } else {
                        //验证是否存在此token，存在以为着已经永远失去效果
                        if (!JWTUtil.isEmpty(token)) {
                            ResultData resultVo = new ResultData();
                            resultVo.setCode(ResultData.TIME_OUT);
                            resultVo.setMsg("登录超时,请重新登录");
                            ResponseUtil.writeJSON(httpServletResponse, resultVo);
                            return;
                        }
                    }
                }
            }
        }
        //放行，进入Security
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

}
